﻿
/// SDMX Artefact Tiny Repository. A repository for SAE Editor.
///    Copyright (C) 2010  Philippe Prati 
///
///    This program is free software: you can redistribute it and/or modify
///    it under the terms of the GNU General Public License as published by
///    the Free Software Foundation, either version 2 of the License, or
///    (at your option) any later version.
///
///    This program is distributed in the hope that it will be useful,
///    but WITHOUT ANY WARRANTY; without even the implied warranty of
///    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
///    GNU General Public License for more details.
///
///    You should have received a copy of the GNU General Public License
///    along with this program.  If not, see <http://www.gnu.org/licenses/>.
///
///    Contact: ph.prati@bluewin.ch
///    
using System;
using System.Collections.Generic;
using System.Xml;

/// <summary>
/// Class for control of SDMX Document artefact presence & attributes extraction
/// </summary>
public class ArtefactLocator
{
    #region XPath

    // XPath expression to select codelist nodes

    private static string queryStructureSets = "//S:StructureSets/structure:StructureSet[@id='{0}'][@agencyID='{1}']";
    private static string queryOrganisationSchemes = "//S:OrganisationSchemes/structure:OrganisationScheme[@id='{0}'][@agencyID='{1}']";
    private static string queryDataflows = "//S:Dataflows/structure:Dataflow[@id='{0}'][@agencyID='{1}']";
    private static string queryDSD = "//S:KeyFamilies/structure:KeyFamily[@id='{0}'][@agencyID='{1}']";
    private static string queryMetadataflows = "//S:Metadataflows/structure:Metadataflow[@id='{0}'][@agencyID='{1}']";
    private static string queryMetadataStructureDefinitions = "//S:MetadataStructureDefinitions/structure:MetadataStructureDefinition[@id='{0}'][@agencyID='{1}']";
    private static string queryCategorySchemes = "//S:CategorySchemes/structure:CategoryScheme[@id='{0}'][@agencyID='{1}']";
    private static string queryConcepts = "//S:Concepts/structure:ConceptScheme[@id='{0}'][@agencyID='{1}']";
    private static string queryCodelists = "//S:CodeLists/structure:CodeList[@id='{0}'][@agencyID='{1}']";
    private static string queryHierarchicalCodelists = "//S:HierarchicalCodelists/structure:HierarchicalCodelist[@id='{0}'][@agencyID='{1}']";
    private static string queryProcesses = "//S:Processes/structure:Process[@id='{0}'][@agencyID='{1}']";
    private static string queryReportingTaxonomies = "//S:ReportingTaxonomies/structure:ReportingTaxonomy[@id='{0}'][@agencyID='{1}']";

    private static string queryMetadataSet = "//genericmetadata:MetadataSet/genericmetadata:Annotations/common:Annotation[common:AnnotationType='{0}']";

    #endregion

    #region Properties

    private XmlDocument xDoc;
    private XmlNamespaceManager nsmgr;

    private TextTypeSdmx _tts;
    public TextTypeSdmx Tts
    {
        get { return _tts; }
    }
    private bool _aggregated;
    public bool Aggregated
    {
        get { return _aggregated; }
    }
    #endregion

    #region C'tor

    public ArtefactLocator(XmlDocument doc)
	{
        xDoc = doc;
        _aggregated = false;
        CreateNameSpaceManager(xDoc);
    }
    #endregion

    #region Helper methods

    /// <summary> Creates an XmlNamespaceManager for resolving namespaces.</summary>
    /// <param name="xd">Associated document</param>
    private void CreateNameSpaceManager(XmlDocument xd)
    {
        nsmgr = new XmlNamespaceManager(xd.NameTable);
        nsmgr.AddNamespace("S", "http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message");
        nsmgr.AddNamespace("structure", "http://www.SDMX.org/resources/SDMXML/schemas/v2_0/structure");
        nsmgr.AddNamespace("common", "http://www.SDMX.org/resources/SDMXML/schemas/v2_0/common");
        nsmgr.AddNamespace("genericmetadata", "http://www.SDMX.org/resources/SDMXML/schemas/v2_0/genericmetadata");
    }
    #endregion

    #region Public Methods

    /// <summary>Locates artefact in submitted document.</summary>
    /// <param name="key">Full ID key</param>
    /// <returns>True if located</returns>
    /// <remarks>Main attributes of artefact are collected in a 'TextTypeSdmx' class instance </remarks>
    public bool LocateArtefact(ArtefactPrimaryKey key)
    {

        CreateNameSpaceManager(xDoc);

        XmlNode nd = null;
        XmlNodeList nodeList = null;

        _aggregated = xDoc.DocumentElement.ChildNodes.Count > 2;
        switch (key.ArtefactType.ToLower())
        {
            case "codelists":
                nodeList = xDoc.SelectNodes(string.Format(queryCodelists, key.Id, key.AgencyId), nsmgr);
                break;
            case "hierarchicalcodelists":
                nodeList = xDoc.SelectNodes(string.Format(queryHierarchicalCodelists, key.Id, key.AgencyId), nsmgr);
                break;
            case "concepts":
                nodeList = xDoc.SelectNodes(string.Format(queryConcepts, key.Id, key.AgencyId), nsmgr);
                break;
            case "categoryschemes":
                nodeList = xDoc.SelectNodes(string.Format(queryCategorySchemes, key.Id, key.AgencyId), nsmgr);
                break;
            case "metadatastructuredefinitions":
                nodeList = xDoc.SelectNodes(string.Format(queryMetadataStructureDefinitions, key.Id, key.AgencyId), nsmgr);
                break;
            case "dataflows":
                nodeList = xDoc.SelectNodes(string.Format(queryDataflows, key.Id, key.AgencyId), nsmgr);
                break;
            case "keyfamilies":
                nodeList = xDoc.SelectNodes(string.Format(queryDSD, key.Id, key.AgencyId), nsmgr);
                break;
            case "metadataflows":
                nodeList = xDoc.SelectNodes(string.Format(queryMetadataflows, key.Id, key.AgencyId), nsmgr);
                break;
            case "organisationschemes":
                nodeList = xDoc.SelectNodes(string.Format(queryOrganisationSchemes, key.Id, key.AgencyId), nsmgr);
                break;
            case "structuresets":
                nodeList = xDoc.SelectNodes(string.Format(queryStructureSets, key.Id, key.AgencyId), nsmgr);
                break;
            case "reportingtaxonomies":
                nodeList = xDoc.SelectNodes(string.Format(queryReportingTaxonomies, key.Id, key.AgencyId), nsmgr);
                break;
            case "processes":
                nodeList = xDoc.SelectNodes(string.Format(queryProcesses, key.Id, key.AgencyId), nsmgr);
                break;
            case "metadataset":
                nodeList = xDoc.SelectNodes(string.Format(queryMetadataSet, "setID"), nsmgr);
                break;
        }

        if (nodeList != null)
        {
            if (key.ArtefactType.ToLower() == "metadataset")
            {
                _tts = new TextTypeSdmx();
                bool valid = _tts.ExtractFromMetadataSet(nodeList[0], nsmgr, key.Id, key.AgencyId, key.Version);
                if (valid)
                    nd = nodeList[0];
            }
            else
            {
                // Normally only one version present or it is an aggregated document
                if (nodeList.Count > 1)
                    _aggregated = true;

                foreach (XmlNode node in nodeList)
                {
                    XmlNode ndVersion = node.Attributes.GetNamedItem("version");

                    // If no version specified it is assumed 1.0 according to SDMX
                    if ((ndVersion == null && key.Version == "1.0") ||
                        (ndVersion != null && ndVersion.Value == key.Version))
                    {
                        nd = node;
                        break;
                    }
                }

                if (nd != null)
                {
                    //  from it extracts names
                    _tts = new TextTypeSdmx(nd, nsmgr, false);
                }
            }
        }

        return nd != null;
    }

    public OwnerGroupDico GenerateOwnerGroupInfo()
    {
        XmlNodeList nodeList = null;
        OwnerGroupDico owd = new OwnerGroupDico();

        if (xDoc.DocumentElement.ChildNodes.Count > 2)
            throw new ApplicationException("Aggregated Organization not supported for Ownership managment!");


        XmlNode ndOrg = xDoc.DocumentElement.ChildNodes[1];
        nodeList = ndOrg.SelectNodes(".//structure:MaintenanceContact", nsmgr);
        foreach (XmlNode nd in nodeList)
        {
            if (nd.ChildNodes.Count > 0)
            {
                // Agency Group
                string agencyId = nd.ParentNode.Attributes["id"].Value;
                if (string.IsNullOrEmpty(agencyId) == false)
                {
                    Group group = null;
                    if (owd.ContainsKey(agencyId) == false)
                    {
                        group = new Group(agencyId);
                        owd.Add(agencyId, group);
                    }
                    else
                        group = owd[agencyId];

                    //User ID of Maintenance is the group administrator
                    XmlNode nodeId = nd.SelectSingleNode("./structure:id", nsmgr);
                    if (nodeId != null)
                    {
                        // Department gives prefix
                        XmlNodeList nodeListDept = nd.SelectNodes("./structure:Department", nsmgr);
                        if (nodeListDept.Count > 0)
                        {
                            string s = nodeListDept[0].InnerText;
                            User u = new User(s + nodeId.InnerText, GroupUserRole.GroupAdmin);
                            // Add in dico
                            group.AddUser(u);
                        }

                        // Others persons are User with writer role
                        XmlNodeList nodeListOthers = nd.ParentNode.SelectNodes("./structure:OtherContact", nsmgr);
                        foreach (XmlNode ndOther in nodeListOthers)
                        {
                            if (ndOther.ChildNodes.Count > 0)
                            {
                                //User ID of Maintenance is the group administrator
                                nodeId = ndOther.SelectSingleNode("./structure:id", nsmgr);
                                if (nodeId != null)
                                {
                                    // Department gives prefix
                                    nodeListDept = ndOther.SelectNodes("./structure:Department", nsmgr);
                                    if (nodeListDept.Count > 0)
                                    {
                                        string s = nodeListDept[0].InnerText;
                                        User u = new User(s + nodeId.InnerText, GroupUserRole.Writer);
                                        // Add in dico
                                        group.AddUser(u);
                                    }
                                }
                            }
                        }
                    }

                }
            }
        }
        return owd;
    }

    #endregion
}
